﻿using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;

namespace PmSoft.Events;

/// <summary>
/// 本地事件总线（用于订阅事件、发布事件）
/// </summary>
/// &gt;
public class LocalEventBus : IEventBus
{
    private static ConcurrentDictionary<string, List<Type>> handlers = new ConcurrentDictionary<string, List<Type>>();
    private readonly IServiceProvider serviceProvider;
    private readonly ILogger logger;

    /// <summary>
    /// 静态构造函数
    /// </summary>
    public LocalEventBus(IServiceProvider serviceProvider, ILogger<LocalEventBus> logger)
    {
        this.serviceProvider = serviceProvider;
        this.logger = logger;
    }

    /// <summary>
    /// 获取事件KEY
    /// </summary>
    /// <typeparam name="TEventArgs"></typeparam>
    /// <returns></returns>
    private string GetEventKey<TEventArgs>()
    {
        return typeof(TEventArgs).FullName;
        //return typeof(TSender).FullName + "-" + typeof(TEventArgs).FullName;
    }

    /// <summary>
    /// 订阅事件
    /// </summary>
    /// <typeparam name="TEventArgs"></typeparam>
    /// <typeparam name="TEventHandler"></typeparam>
    public void Subscribe<TEventArgs, TEventHandler>()
        where TEventArgs : EventArgs
        where TEventHandler : IEventHandler<TEventArgs>
    {
        //if (!typeof(TSender).IsSerializable)
        //    throw new SerializationException($"{typeof(TSender).FullName} 未标记可序列化.");

        //if (!typeof(TEventArgs).IsSerializable)
        //    throw new SerializationException($"{typeof(TEventArgs).FullName} 未标记可序列化.");

        string eventKey = GetEventKey<TEventArgs>();
        Type handlerType = typeof(TEventHandler);
        List<Type>? handlerTypes;
        if (handlers.ContainsKey(eventKey) && handlers.TryGetValue(eventKey, out handlerTypes))
        {
            handlerTypes.Add(handlerType);
            handlers[eventKey] = handlerTypes;
        }
        else
        {
            handlerTypes = new List<Type> { handlerType };
            handlers.TryAdd(eventKey, handlerTypes);
        }
    }

    /// <summary>
    /// 发布事件到后台执行
    /// </summary>
    /// <typeparam name="TEventArgs"></typeparam>
    /// <param name="sender"></param>
    /// <param name="eventArgs"></param>
    /// <returns></returns>
    public bool PublishTo<TEventArgs>(object sender, TEventArgs eventArgs)
        where TEventArgs : EventArgs
    {
        string eventKey = GetEventKey<TEventArgs>();

        if (!handlers.ContainsKey(eventKey))
            return false;

        var handlerTypes = handlers[eventKey];
        var task = new Task(async () =>
        {
            using (IServiceScope serviceScope = serviceProvider.CreateScope())
            {
                foreach (Type handlerType in handlerTypes)
                {
                    var handler = serviceScope.ServiceProvider.GetService(handlerType) as IEventHandler<TEventArgs>;
                    if (null != handler)
                    {
                        await handler.HandleAsync(sender, eventArgs).ContinueWith(task =>
                        {
                            task.Exception?.Handle(exception =>
                            {
                                logger.LogError(exception, "执行触发操作事件时发生异常");
                                return true;
                            });
                        }).ConfigureAwait(false);
                    }
                }
            }
        });
        task.Start();
        return true;
    }

    /// <summary>
    /// 发布事件
    /// </summary>
    /// <typeparam name="TEventArgs"></typeparam>
    /// <param name="sender"></param>
    /// <param name="eventArgs"></param>
    /// <returns></returns>
    public async Task<bool> PublishAsync<TEventArgs>(object sender, TEventArgs eventArgs)
        where TEventArgs : EventArgs
    {
        string eventKey = GetEventKey<TEventArgs>();

        if (!handlers.ContainsKey(eventKey))
            return false;

        var handlerTypes = handlers[eventKey];

        using (IServiceScope serviceScope = serviceProvider.CreateScope())
        {
            foreach (Type handlerType in handlerTypes)
            {
                var handler = serviceScope.ServiceProvider.GetService(handlerType) as IEventHandler<TEventArgs>;
                if (null != handler)
                {
                    await handler.HandleAsync(sender, eventArgs).ContinueWith(task =>
                    {
                        task.Exception?.Handle(exception =>
                        {
                            logger.LogError(exception, "执行触发操作事件时发生异常");
                            return true;
                        });
                    }).ConfigureAwait(true);
                }
            }
        }
        return true;
    }
}